#include "board.h"
#include "boot.h"


uint8_t  update_type=0;          //0:app  1:logic
uint32_t firmware_address=0;     //固件起始地址
uint32_t firmware_length=0;      //固件长度
uint32_t firmware_package_cnt=0; //固件总包号
uint32_t firmware_crc=0;         //CRC

uint8_t rf_buffer[255];

static BootAddr jump_to_addr;

static bool checkReadProtect()
{
  if (FLASH_IsReadProtected()) {
    dbg_printf("  Noops, memory in read protect mode.\r\n");
    return false;
  }
  return true;
}

static void checkFlashBusy()
{
  BootWord fast_pg = FLASH->CR&FLASH_CR_FASTPG;
  FLASH->CR &= ~(FLASH_CR_FASTPG);
  while (FLASH_IsBusy());
  FLASH->CR |= fast_pg;
}

static void resetBoot()
{
  checkFlashBusy();
  SYS_APBResetAndDisable(APB_MASK_UART0);
  SYS_APBResetAndDisable(APB_MASK_GPIOx(UART0_UARTRXD_AF_GPIO));
}

static void waitAndResetDevice()
{
  resetBoot();
  SYS_SoftwareReset();
} 


void bootEnd()
{
  dbg_printf("bootEnd\r\n");
  if (jump_to_addr != (BootAddr)(-1)) {
    dbg_printf("Jump to 0x%x\r\n", jump_to_addr);
  }

  resetBoot();

#ifdef BOOT_DEBUG
  UTIL_StopLogger();
#endif
  SYS_SoftwareReset();
}
//********************************************************
// 返回数据
//********************************************************
static uint8_t sendACKUPdata(void)
{
  rf_buffer[0] = 0xAB;
  rf_buffer[1] = 0x02;
  rf_buffer[2] = 0x7F;
  rf_buffer[3] = 0x00;
  return rf_transmit(rf_buffer,4,100);
}

static uint8_t sendACKwrite(uint32_t packid, uint8_t ret)
{
  rf_buffer[0] = 0xAB;
  rf_buffer[1] = 0x06;
  rf_buffer[2] = 0x31;
  rf_buffer[3] = packid>>24;
  rf_buffer[4] = packid>>16;
  rf_buffer[5] = packid>>8;
  rf_buffer[6] = packid;
  rf_buffer[7] = ret;
  return rf_transmit(rf_buffer,8,100);
}

//********************************************************
// 擦除指令
//********************************************************
static BootErrType eraseFlashCmd(void)
{
  BootAddr eraseLen;
  if (!checkReadProtect()) {
    return BOOT_ERR_NACK;
  }

  /*根据升级类型确定擦除地址*/
  //if(update_type==0)
  //{
    firmware_address = DFU_APP_CODE_ADDRESS;
  //}
  //else
  //{
    //firmware_address = DFU_APP_LOGIC_ADDRESS;
  //}

  /*擦除*/
  FLASH_Unlock();
  FLASH_Erase(firmware_address, firmware_length);
  FLASH_Erase(DFU_APP_PARAM, 8);
  FLASH_Lock();
  return BOOT_ERR_OK;
}

//********************************************************
// 写flash指令
//********************************************************
static BootErrType bootWriteMemory(BootAddr addr, BootLen len, const BootByte *vals)
{ 
  FLASH_Unlock();
  FLASH_FastProgram(addr, (uint32_t)vals, len);
  FLASH_Lock();
  return BOOT_ERR_OK;
}

static void writeMemoryCmd(uint32_t addr,uint8_t *buffer,uint16_t len)
{
  bootWriteMemory(addr, len, buffer);
}

//********************************************************
// 计算crc指令
//********************************************************
static BootWord bootCalcCrc(BootAddr addr, BootAddr len)
{
  dbg_printf(" bootCalcCrc 0x%x %d\r\n", addr, len);
  
  SYS_EnableAHBClock(AHB_MASK_CRC0);
  CRC_Reset(CRC0);
  CRC_SetEndian(CRC0, CRC_CR_LITTLE_ENDIAN);
  CRC_SetInitData(CRC0, 0xFFFFFFFF);
  CRC_SetPolyCoef(CRC0, 0x04C11DB7);
  CRC_SetReverseInput(CRC0, CRC_CR_REV_IN_WORD);
  CRC_SetOutputDataReverseMode(CRC0, CRC_CR_REV_OUT_BIT);

  uint8_t *flash_add = (uint8_t *)addr;
  for (int i = 0; i < len; i++)
  {
    CRC_FeedData8(CRC0, flash_add[i]);
  }

  uint32_t crc = CRC_ReadData32(CRC0);
  SYS_DisableAHBClock(AHB_MASK_CRC0);

  return crc;
}

static uint32_t calculateCrcCmd(uint32_t addr, uint32_t len)
{
  BootWord crc = 0;
  
  crc = bootCalcCrc(addr, len);
  dbg_printf("calculateCrcCmd done!\r\n");
  return crc;
}

//********************************************************
// 复位指令
//********************************************************
static void resetDeviceCmd()
{
  dbg_printf("resetDeviceCmd\r\n");
  waitAndResetDevice();
}

//********************************************************
// 写app信息
//********************************************************
static void WriteAppInfo(void)
{
   uint8_t  para[8];
   para[0] = firmware_length>>24;
   para[1] = firmware_length>>16;
   para[2] = firmware_length>>8;
   para[3] = firmware_length;
   para[4] = firmware_crc>>24;
   para[5] = firmware_crc>>16;
   para[6] = firmware_crc>>8;
   para[7] = firmware_crc;
   writeMemoryCmd(DFU_APP_PARAM,para,8);
}

void boot()
{
  uint32_t  lastfirmpackid=0;
  uint32_t receive_firmpackid=0;
  uint32_t receive_firmlengadd=0;
  uint32_t updateaddr=0;
  uint32_t crc=0;
  uint8_t  rereceivecnt=0;
  uint32_t rcvdiff=0;

  jump_to_addr = (BootAddr)(-1);

  /*擦除成功则发送确认包，否则重启*/
  if(BOOT_ERR_OK==eraseFlashCmd())
  {
     WDOG_Feed();
     sendACKUPdata();
     updateaddr = firmware_address;
     while (1) 
     {
       WDOG_Feed();
       /*等待接收指令 10s超时*/
       if((rf_receive(rf_buffer, 3000)>0)&&(0x31==rf_buffer[2])&&(0xAB==rf_buffer[0]))
       {
          printf("1.rcv packat diff %d ms\r\n",(UTIL_McycleToUs(UTIL_GetMcycle())-rcvdiff)/1000);
          rcvdiff = UTIL_McycleToUs(UTIL_GetMcycle());
          receive_firmpackid = (rf_buffer[3]<<24) | (rf_buffer[4]<<16) |  (rf_buffer[5]<<8) | rf_buffer[6];
          receive_firmlengadd= rf_buffer[7];
   
          /*包号连续，写flash*/
          if(((receive_firmpackid-lastfirmpackid)==1)||(receive_firmpackid==0x00))
          {
             lastfirmpackid = receive_firmpackid;
             writeMemoryCmd(updateaddr, rf_buffer+8,receive_firmlengadd);
             updateaddr += receive_firmlengadd;
             
             printf("2.write flash cost %d ms\r\n",(UTIL_McycleToUs(UTIL_GetMcycle())-rcvdiff)/1000);
             rcvdiff = UTIL_McycleToUs(UTIL_GetMcycle());
             if(receive_firmpackid!=(firmware_package_cnt-1))
             {
               sendACKwrite(receive_firmpackid, 0x00);
             }
             printf("3.respond  cost %d ms\r\n",(UTIL_McycleToUs(UTIL_GetMcycle())-rcvdiff)/1000);
          }
         
          /*如果包完全发送完毕*/
          if(receive_firmpackid==(firmware_package_cnt-1))
          {
             crc = calculateCrcCmd(firmware_address, firmware_length);
             if(crc==firmware_crc)
             {
               sendACKwrite(receive_firmpackid, 0x00);
               WriteAppInfo();
             }
             else
             {
               sendACKwrite(receive_firmpackid, 0x02);
             }
             break;
          }
       }
       else
       {
         rereceivecnt++;
         if(rereceivecnt>=4)        //4次重收
           break;
       }
     }
  }
}